﻿namespace Chinook.Facade
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Xml;
    using System.Xml.Linq;

    using Chinook.Core;
    using Chinook.Facade.Messaging;
    using Chinook.Facade.ViewModel;

    public class ProfilerService : IProfilerService
    {
        #region Fields

        public const string xml_NameSpace = @"<Logs xmlns:log4j=""http://www.w3.org/TR/html4/"">";
        public const string xml_end_tag = @"</Logs>";
        public const string xml_start_tag = @"<?xml version=""1.0""?>";

        List<LogData> _logs;
        string _path;

        #endregion Fields

        #region Constructors

        public ProfilerService(string path)
        {
            _path = path;
        }

        #endregion Constructors

        #region Properties

        public List<LogData> Logs
        {
            set{_logs = GetAll() ;}
            get{return (_logs == null) ? GetAll() : _logs;}
        }

        #endregion Properties

        #region Methods

        public List<string> DistinctClasses()
        {
            return Logs.GroupBy(log => log.ClassName).Select(grp => grp.First()).Select(mn => mn.ClassName).ToList();
        }

        public List<string> DistinctMethods(string className)
        {
            return GetAll(className).GroupBy(log => log.MethodName).Select(grp => grp.First()).Select(mn => mn.MethodName).ToList();
        }

        public List<Chinook.Core.LogData> GetAll()
        {
            XmlReader log = GetLogAsXmlReader(_path);
            XNamespace log4j = "http://www.w3.org/TR/html4/";
            XElement main = XElement.Load(log);
            List<LogData> logs = new List<LogData>();
            var query = from w in main.Descendants(log4j + "event")
                        select w;
            foreach (var record in query)
            {
                LogData logData = new LogData();
                logData.Message = (string)record;
                logData.TimeStamp = Base.Utils.CommonUtils.StrToLong(((string)record.Attribute("timestamp")).Substring(0, 10));
                logData.ClassName = Regex.Split(logData.Message, "] ")[0].Replace("[", "");
                logData.MethodName = logData.ClassName.Split('.')[logData.ClassName.Split('.').Length - 1];
                logData.ClassName = logData.ClassName.Replace("." + logData.MethodName, "");
                logData.ExecutionTime = Base.Utils.CommonUtils.StrToDouble(Regex.Split(logData.Message, "=")[1]);
                logs.Add(logData);
            }
            return logs;
        }

        public List<Chinook.Core.LogData> GetAll(string className)
        {
            return Logs.FindAll(log => log.ClassName.Equals(className));
        }

        public List<Chinook.Core.LogData> GetAll(string className, string methodName)
        {
            return Logs.FindAll(log => log.ClassName.Equals(className) && log.MethodName.Equals(methodName));
        }

        public List<Chinook.Core.LogData> GetAllByTimeRange(DateTime start, DateTime end, string className, string methodName)
        {
            if (end != DateTime.MinValue)
            {
                return GetAll(className, methodName).FindAll(log => log.ExecutionDateAndTime >= start && log.ExecutionDateAndTime <= end);
            }
            else
            {
                return GetAll(className, methodName).FindAll(log => log.ExecutionDateAndTime >= start);
            }
        }

        public XmlReader GetLogAsXmlReader(string path)
        {
            return XmlReader.Create(new StringReader(GetLogAsXmlString(path)));
        }

        public string GetLogAsXmlString(string path)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(xml_start_tag);
            sb.Append(xml_NameSpace);
            sb.Append(Base.Utils.CommonUtils.GetResourceAsString(path));
            sb.Append(xml_end_tag);
            return sb.ToString();
        }

        private List<ProfilerViewModel> GetAsViewModel(List<LogData> input)
        {
            List<ProfilerViewModel> result = new List<ProfilerViewModel>();

            foreach (var log in input)
            {
                ProfilerViewModel vm = new ProfilerViewModel();
                vm.ClassName = log.ClassName;
                vm.ExecutionDateAndTime = log.ExecutionDateAndTime;
                vm.ExecutionTime = log.ExecutionTime;
                vm.MethodName = log.MethodName;
                vm.TimeStamp = log.TimeStamp;
                result.Add(vm);
            }

            return result;
        }

    public  ProfilerResponse GetProfilerLogs(ProfilerRequest profilerRequest)
        {
            ProfilerResponse response = new ProfilerResponse();
            try
            {
                if (profilerRequest.Start != DateTime.MinValue)
                {
                    response.ProfilerViewModels = GetAsViewModel(GetAllByTimeRange(profilerRequest.Start, profilerRequest.End,profilerRequest.ClassName, profilerRequest.MethodName));
                }
                else
                {
                    response.ProfilerViewModels = GetAsViewModel(GetAll(profilerRequest.ClassName, profilerRequest.MethodName));
                }
                response.Success = true;
            }
            catch (Exception ex)
            {
                response.Success = false;
                response.Message = ex.StackTrace;
            }

            return response;
        }

        #endregion Methods
    }
}